﻿using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using MediaBrowser.CustomControls.CustomEvent;
using System.Collections.Generic;

namespace MediaBrowser.CustomControls.ScrollControl
{
    /// <summary>
    /// Composant Scrollable permettant d'afficher des images
    /// </summary>
    public partial class ScrollerControl : UserControl
    {
        Storyboard boardScroll;
        private ColumnedPanel mainPanel = null;
        private ItemsControl ItemsControl
        {
            get
            {
                return itemsControl;
            }
        }
        private DispatcherTimer animationTimer = new DispatcherTimer();
        private double velocity;

        public ScrollerControl()
        {
            InitializeComponent();
            Count = 0;
            ValueCheckFilter = false;
            AllowDrop = true;
            boardScroll = new Storyboard();
            this.DragOver += ScrollerControl_DragOver;
            this.Drop += ScrollerControl_Drop;
            this.PreviewKeyDown += ScrollerControl_KeyDown;
            velocity = 0;
            animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 20);
            animationTimer.Tick += HandleWorldTimerTick;
        }

        #region Event ScrollerControl

        /// <summary>
        /// Suppression Image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ScrollerControl_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Delete)
            {
                CurrentItemEventArgs args = new CurrentItemEventArgs(ItemsDeleteEvent, 0);
                RaiseEvent(args);
            }
        }

        /// <summary>
        /// DragOver une nouvelle Image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ScrollerControl_DragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                foreach (String fileName in (e.Data.GetData(DataFormats.FileDrop, true) as String[]))
                {
                    if (fileName.IsImageFile())
                    {
                        e.Effects = DragDropEffects.Copy;
                    }
                    else
                    {
                        e.Effects = DragDropEffects.None;
                        break;
                    }
                }
            }
            else { e.Effects = DragDropEffects.None; }
            e.Handled = true;

        }

        /// <summary>
        /// Drop une nouvelle Image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ScrollerControl_Drop(object sender, DragEventArgs e)
        {
            string[] fileNames = e.Data.GetData(DataFormats.FileDrop, true) as string[];

            foreach (string fileName in fileNames)
            {
                Uri uri = new Uri(fileName, UriKind.RelativeOrAbsolute);
                CurrentItemDrop = fileName;
                DropItemEventArgs args = new DropItemEventArgs(ItemsDropEvent, fileName);
                RaiseEvent(args);

            }
            this.Scroll(new Point() { X = -mainPanel.ActualWidth, Y = -mainPanel.ActualHeight });
        }

        #endregion

        #region Event Zoom

        private void zoomOut_Click(object sender, RoutedEventArgs e)
        {
            boardScroll.SkipToFill();
            boardScroll.Children.Clear();

            foreach (ItemHolder item in ItemsControl.Items)
            {
                List<Timeline> lTmp = null;
                lTmp = item.GetZoomOutAnimation(20);

                foreach (Timeline t in lTmp)
                {
                    boardScroll.Children.Add(t);
                }
            }
            boardScroll.Begin();
        }

        private void zoomFit_Click(object sender, RoutedEventArgs e)
        {
            boardScroll.SkipToFill();
            boardScroll.Children.Clear();

            foreach (ItemHolder item in ItemsControl.Items)
            {
                List<Timeline> lTmp = null;
                lTmp = item.GetZoomFitAnimation();

                foreach (Timeline t in lTmp)
                {
                    boardScroll.Children.Add(t);
                }
            }
            boardScroll.Begin();
        }

        private void zoomIn_Click(object sender, RoutedEventArgs e)
        {
            boardScroll.SkipToFill();
            boardScroll.Children.Clear();

            foreach (ItemHolder item in ItemsControl.Items)
            {
                List<Timeline> lTmp = null;
                lTmp = item.GetZoomInAnimation(20);

                foreach (Timeline t in lTmp)
                {
                    boardScroll.Children.Add(t);
                }
            }
            boardScroll.Begin();
        }


        #endregion

        #region Private Methods

        /// <summary>
        /// Lors de l'ouverture on garde la référence mainPanel pour une utilisation ultérieur
        /// </summary>
        private void OnPanelLoaded(object sender, RoutedEventArgs e)
        {
            // Grab a reference to the TabPanel3D when it loads.
            mainPanel = sender as ColumnedPanel;
            mainPanel.SetValue(Control.VerticalAlignmentProperty, VerticalAlignment.Center);
        }

        /// <summary>
        /// Au changement de l'ItemsSource alimentation des Images dans le composant
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void ItemsSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ScrollerControl tc = sender as ScrollerControl;
            if (tc == null)
                return;

            TypeFilter myFilter = new TypeFilter(InterfaceFilter);
            Type[] interfaces = tc.ItemsSource.GetType().FindInterfaces(myFilter, "System.Collections.Specialized.INotifyCollectionChanged");
            if (interfaces.Length > 0)
            {
                ((INotifyCollectionChanged)tc.ItemsSource).CollectionChanged += tc.ItemsSource_CollectionChanged;
            }
            if (e.OldValue != null)
            {
                tc.ClearItems();
            }
            if (e.NewValue != null)
            {
                tc.BindItems((IEnumerable)e.NewValue);
            }
        }

        /// <summary>
        /// Suppression de tous les Items
        /// </summary>
        private void ClearItems()
        {
            ItemsControl.Items.Clear();
        }

        /// <summary>
        /// Binding des éléments
        /// </summary>
        /// <param name="items"></param>
        private void BindItems(IEnumerable items)
        {
            if (!String.IsNullOrEmpty(MemberBinding))
            {
                foreach (var item in items)
                {
                    TypeFilter myFilter = new TypeFilter(InterfaceFilter);
                    Type[] interfaces = item.GetType().FindInterfaces(myFilter, "System.ComponentModel.INotifyPropertyChanged");
                    if (interfaces.Length > 0)
                    {
                        ((INotifyPropertyChanged)item).PropertyChanged += Item_PropertyChanged;
                    }
                    PropertyInfo pi = item.GetType().GetProperty(MemberBinding);
                    string value = (string)(pi.GetValue(item, null));

                    if (!String.IsNullOrEmpty(value))
                    {
                        //ItemHolder
                        ItemHolder itemHolder = new ItemHolder();
                        FileInfo f = new FileInfo(value);
                        itemHolder.File = f;
                        itemHolder.Margin = new Thickness(4);
                        itemHolder.ToolTip = f.Name;
                        itemHolder.Tag = item;
                        itemHolder.OnClickItem += itemHolder_OnClickItem;
                        itemHolder.OnMouseEnterItem += itemHolder_OnMouseEnterItem;
                        ItemsControl.Items.Add(itemHolder);
                        Count++;
                    }
                }
            }
            else
            {
                throw new ArgumentNullException("Error Binding", "MemberBinding must be defined !");
            }
        }

        /// <summary>
        /// Filtre sur le contenu vide ou pas d'une propriété
        /// Ex: Filtre sur la présence d'une propriété description ou pas
        /// </summary>
        /// <param name="tc"></param>
        private void CheckFilter()
        {
            if (!String.IsNullOrEmpty(MemberCheckFilterBinding))
            {
                foreach (ItemHolder item in ItemsControl.Items)
                {
                    string value = String.Empty;
                    PropertyInfo pi = item.Tag.GetType().GetProperty(MemberCheckFilterBinding);
                    value = (string)(pi.GetValue(item.Tag, null));

                    if (!item.IsHide)
                    {
                        if (ValueCheckFilter)
                        {
                            if (String.IsNullOrEmpty(value))
                            {
                                item.Hide();
                                Count--;
                            }
                        }
                        else
                        {
                            if (!String.IsNullOrEmpty(value))
                            {
                                item.Hide();
                                Count--;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Fin animation de suppression d'un ItemHolder => Suppression de la liste
        /// </summary>
        /// <param name="sender"></param>
        void q_OnHideAnimationEnd(object sender)
        {
            ItemHolder q = sender as ItemHolder;
            ItemsControl.Items.Remove(q);
        }

        /// <summary>
        /// Filtre sur une ou plusieurs proprietés
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void FilterChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ScrollerControl tc = sender as ScrollerControl;
            if (tc == null)
                return;

            tc.RefreshFilterData();
        }

        /// <summary>
        /// Au passage de la souris, déclenchement de la command SelectCommandParameter
        /// </summary>
        /// <param name="sender"></param>
        void itemHolder_OnMouseEnterItem(object sender)
        {
            ItemHolder itemHolder = sender as ItemHolder;
            if (SelectCommand != null)
            {
                SelectCommandParameter = itemHolder.Tag;
                SelectCommand.Execute(SelectCommandParameter);
            }
            Focusable = true;
            Focus();
        }

        /// <summary>
        /// Sur le clique d'un ItemHolder, déclenchement de l'évenement ItemsSelectedEvent
        /// </summary>
        /// <param name="sender"></param>
        private void itemHolder_OnClickItem(object sender)
        {
            ItemHolder itemHolder = sender as ItemHolder;
            int idx = this.itemsControl.Items.IndexOf(itemHolder);
            CurrentItemEventArgs args =
            new CurrentItemEventArgs(ItemsSelectedEvent, idx + 1);
            RaiseEvent(args);
        }

        /// <summary>
        /// Evenement Modification(ajout/suppression) ItemsSource
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                for (int i = 0; i <= e.NewItems.Count - 1; i++)
                {
                    object item = e.NewItems[i];
                    PropertyInfo pi = item.GetType().GetProperty(MemberBinding);
                    string value = (string)(pi.GetValue(item, null));
                    if (!String.IsNullOrEmpty(value))
                    {
                        //ItemHolder
                        ItemHolder itemHolder = new ItemHolder();
                        FileInfo f = new FileInfo(value);
                        itemHolder.File = f;
                        itemHolder.Margin = new Thickness(4);
                        itemHolder.ToolTip = f.Name;
                        itemHolder.Tag = item;
                        itemHolder.OnClickItem += itemHolder_OnClickItem;
                        itemHolder.OnMouseEnterItem += itemHolder_OnMouseEnterItem;
                        ItemsControl.Items.Insert(0, itemHolder);
                        Count++;
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                for (int i = 0; i <= e.OldItems.Count - 1; i++)
                {
                    object item = e.OldItems[i];
                    foreach (ItemHolder q in ItemsControl.Items)
                    {
                        if (q.Tag.Equals(item))
                        {
                            q.OnHideAnimationEnd += q_OnHideAnimationEnd;
                            q.Hide();
                            Count--;
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Un des propriétés de la liste est modifié, rafraichissement des filtres
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            foreach (ItemHolder item in ItemsControl.Items)
            {
                if (sender.Equals(item.Tag))
                {
                    string value = String.Empty;
                    PropertyInfo pi = item.Tag.GetType().GetProperty(MemberCheckFilterBinding);
                    value = (string)(pi.GetValue(item.Tag, null));

                    if (!ValueCheckFilter)
                    {
                        if (!String.IsNullOrEmpty(value))
                        {
                            if (!item.IsHide)
                            {
                                item.Hide();
                                Count--;
                            }
                        }
                        else
                        {
                            if (item.IsHide)
                            {
                                item.Show();
                                Count++;
                            }
                        }
                    }
                    else
                    {
                        if (!String.IsNullOrEmpty(value))
                        {
                            if (item.IsHide)
                            {
                                item.Show();
                                Count++;
                            }
                        }
                        else
                        {
                            if (!item.IsHide)
                            {
                                item.Hide();
                                Count--;
                            }
                        }
                    }
                    break;
                }
            }
        }

        /// <summary>
        /// Contenu <see cref="FrictionScrollViewer">FrictionScrollViewer</see>
        /// défile par le delta. Ce delta est réglé par le
        /// <see cref="ScrollerControlAdorner"> ScrollerControlAdorner</see> 
        /// associé à ce contrôle
        /// </summary>
        /// <param name="delta">Valeur du Delta</param>
        private void Scroll(Point delta)
        {
            // Scroll vers la nouvelle position
            ScrollViewer.ScrollToHorizontalOffset(ScrollViewer.HorizontalOffset + delta.X);
            ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset + delta.Y);
        }

        private static bool InterfaceFilter(Type typeObj, Object criteriaObj)
        {
            if (typeObj.ToString() == criteriaObj.ToString())
                return true;
            else
                return false;
        }

        /// <summary>
        /// ScrollButton Value Changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScrollButton_OnValueChangeEvent(object sender, ValueChangeEventArgs e)
        {
            if (e.Value != 0)
            {
                velocity = (50 * e.Value) / 100;
                animationTimer.Start();
            }
            else
            {
                animationTimer.Stop();
            }
        }

        private void HandleWorldTimerTick(object sender, EventArgs e)
        {
            this.Scroll(new Point() { X = velocity, Y = 0 });
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Rafrachissement du composant en fonction des filtres
        /// </summary>
        public void RefreshFilterData()
        {
            if (!String.IsNullOrEmpty(MemberFilterBinding))
            {
                Count = ItemsControl.Items.Count;
                foreach (ItemHolder item in ItemsControl.Items)
                {
                    if (String.IsNullOrEmpty(Filter))
                    {
                        item.Show();
                    }
                    else if (!String.IsNullOrEmpty(Filter))
                    {
                        string[] members = MemberFilterBinding.Split(',');
                        bool noFind = false;

                        for (int i = 0; i <= members.Count() - 1; i++)
                        {
                            PropertyInfo pi = item.Tag.GetType().GetProperty(members[i]);
                            string value = (string)(pi.GetValue(item.Tag, null));

                            if (!value.ToUpper().Contains(Filter.ToUpper()))
                            {
                                noFind = true;
                                item.Hide();
                            }
                            else
                            {
                                item.Show();
                                noFind = false;
                                break;
                            }
                        }
                        if (noFind)
                        {
                            Count--;
                        }
                    }
                }
            }
            else
            {
                throw new ArgumentNullException("Error Binding", "MemberFilterBinding must be defined !");
            }
            CheckFilter();
        }

        #endregion

        #region DPs

        /// <summary>
        /// Propriété Bindé utilisé pour charger les Images
        /// Ex : Une propriété Url
        /// </summary>
        public String MemberBinding
        {
            get { return (String)GetValue(MemberBindingProperty); }
            set { SetValue(MemberBindingProperty, value); }
        }

        public static DependencyProperty MemberBindingProperty =
        DependencyProperty.Register("MemberBinding", typeof(String), typeof(ScrollerControl));

        /// <summary>
        /// Liste des Objets à Binder
        /// </summary>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ScrollerControl),
        new PropertyMetadata(ItemsSourceChanged));

        /// <summary>
        /// Command Property Dependency Property
        /// </summary>
        public static readonly DependencyProperty SelectCommandProperty = DependencyProperty.Register("SelectCommand", typeof(ICommand), typeof(ScrollerControl), new UIPropertyMetadata(null));

        /// <summary>
        /// Parameter pour ICommand
        /// </summary>
        public static readonly DependencyProperty SelectCommandParameterProperty = DependencyProperty.Register("SelectCommandParameter", typeof(object), typeof(ScrollerControl), new UIPropertyMetadata(null));

        /// <summary>
        /// Gets ou sets SelectCommand property. 
        /// </summary>
        public ICommand SelectCommand
        {
            get { return (ICommand)GetValue(SelectCommandProperty); }
            set { SetValue(SelectCommandProperty, value); }
        }

        /// <summary>
        /// Gets ou sets SelectCommandParameter property.
        /// </summary>
        public object SelectCommandParameter
        {
            get { return GetValue(SelectCommandParameterProperty); }
            set { SetValue(SelectCommandParameterProperty, value); }
        }

        public String MemberFilterBinding
        {
            get { return (String)GetValue(MemberFilterBindingProperty); }
            set { SetValue(MemberFilterBindingProperty, value); }
        }

        public static DependencyProperty MemberFilterBindingProperty =
        DependencyProperty.Register("MemberFilterBinding", typeof(String), typeof(ScrollerControl));

        public String Filter
        {
            get { return (String)GetValue(FilterProperty); }
            set { SetValue(FilterProperty, value); }
        }

        public static DependencyProperty FilterProperty =
        DependencyProperty.Register("Filter", typeof(String), typeof(ScrollerControl),
        new PropertyMetadata(FilterChanged));

        public int Count
        {
            get { return (int)GetValue(CountProperty); }
            set { SetValue(CountProperty, value); }
        }

        public static DependencyProperty CountProperty =
        DependencyProperty.Register("Count", typeof(int), typeof(ScrollerControl));

        public String MemberCheckFilterBinding
        {
            get { return (String)GetValue(MemberCheckFilterBindingProperty); }
            set { SetValue(MemberCheckFilterBindingProperty, value); }
        }

        public static DependencyProperty MemberCheckFilterBindingProperty =
        DependencyProperty.Register("MemberCheckFilterBinding", typeof(String), typeof(ScrollerControl),
        new PropertyMetadata(FilterChanged));

        public bool ValueCheckFilter
        {
            get { return (bool)GetValue(ValueCheckFilterProperty); }
            set { SetValue(ValueCheckFilterProperty, value); }
        }

        public static DependencyProperty ValueCheckFilterProperty =
        DependencyProperty.Register("ValueCheckFilter", typeof(bool), typeof(ScrollerControl),
        new PropertyMetadata(FilterChanged));

        public String CurrentItemDrop
        {
            get { return (String)GetValue(CurrentItemDropProperty); }
            set { SetValue(CurrentItemDropProperty, value); }
        }

        public static DependencyProperty CurrentItemDropProperty =
        DependencyProperty.Register("CurrentItemDrop", typeof(String), typeof(ScrollerControl));

        #endregion

        #region Created Events

        public static readonly RoutedEvent ItemsSelectedEvent =
            EventManager.RegisterRoutedEvent(
            "OnItemsSelected", RoutingStrategy.Bubble,
            typeof(CurrentItemEventHandler),
            typeof(ScrollerControl));

        //add remove handlers
        public event CurrentItemEventHandler OnItemsSelected
        {
            add { AddHandler(ItemsSelectedEvent, value); }
            remove { RemoveHandler(ItemsSelectedEvent, value); }
        }

        public static readonly RoutedEvent ItemsDropEvent =
            EventManager.RegisterRoutedEvent(
            "OnItemsDrop", RoutingStrategy.Bubble,
            typeof(DropItemEventHandler),
            typeof(ScrollerControl));

        //add remove handlers
        public event DropItemEventHandler OnItemsDrop
        {
            add { AddHandler(ItemsDropEvent, value); }
            remove { RemoveHandler(ItemsDropEvent, value); }
        }

        public static readonly RoutedEvent ItemsDeleteEvent =
            EventManager.RegisterRoutedEvent(
            "OnItemsDelete", RoutingStrategy.Bubble,
            typeof(CurrentItemEventHandler),
            typeof(ScrollerControl));

        //add remove handlers
        public event CurrentItemEventHandler OnItemsDelete
        {
            add { AddHandler(ItemsDeleteEvent, value); }
            remove { RemoveHandler(ItemsDeleteEvent, value); }
        }

        #endregion



    }
}
